/*
 *   ,           ,   w3ld.c - live website monitoring and manipulation
 *  /             \  Copyright  (C) 2002 Andrew Morrison
 * ((__-^^-,-^^-__))
 * `-_---'  `---_-'  This program is free software; you can redistribute it and/or
 *  `--|o`   'o|--'  modify it under the terms of the   GNU General Public License
 *      \  `  /      as published by the Free   Software Foundation; either version 2
 *       ): :(       of the License, or (at your option) any later version.
 *       :o_o:
 *        "-"
 *                   This program is distributed in the hope that it will be useful,
 *                   but WITHOUT ANY WARRANTY;  without even the implied warranty of
 *                   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See  the
 *                   GNU General Public License for more details.
 *
 *                   The GPL may be obtained at http://www.gnu.org/copyleft/gpl.html
 */
/*! @file w3ld.c
 *	\brief Initialize and launch all program functions
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <assert.h>

#define _REENTRANT
#include <pthread.h>
#include <signal.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

// Strange, right? ...
#include <gnome.h>

#include "getopt.h"

#include <gr_graph_fun.h>
#include <LOGR_functions.h>

#include "perror_exit.h"
#include "w3ld.h"
#include "server_protocol.h"
#include "func_lexer.h"
#include "user_functions.h"
#include "watch.h"

pthread_mutex_t mutex;
global_frame frame;

short create_socket(options *opts, int *tsd, struct sockaddr_in *sin, int *len) {
/*
 * input:   a port, a descriptor pointer, a sockaddr_in and a socket length pointer.
 * output:  A socket descriptor for our IPC needs.
 */
	*len = sizeof(*sin);
	if((*tsd = socket(AF_INET,SOCK_STREAM,0)) < 0)
		perror_exit("%s %d",__FILE__,__LINE__);

	memset(sin,'\0',*len);
	(*sin).sin_family = AF_INET;
	(*sin).sin_addr.s_addr = INADDR_ANY;
	(*sin).sin_port = htons(opts->port);

	if(bind(*tsd,(struct sockaddr *)sin, *len) < 0) {
		fprintf(stderr,"Could not bind to port %d\n",opts->port);
		return 0;
	}

	if(listen(*tsd,10) < 0)
		perror_exit("%s %d",__FILE__,__LINE__);
	
	return 1;
}

void
check_watches(log_line *line)
{
/* For each watch, if active, perform its check */
	LNode_type *ptr = NULL;
	
	for(ptr=(frame.watches)->back;ptr!=frame.watches;ptr=ptr->back)
		if(((w3lWatch *)(ptr->info))->active)
			((w3lWatch *)(ptr->info))->check(((w3lWatch *)(ptr->info)),line);
}

void
insert_text(GtkTextBuffer *buffer,GtkWidget *textView, GtkTextIter *iter,gchar *txt)
{
	int lines;
	
	return;
	
	if(!buffer || !textView || !iter || !txt)
		return;
	
	if((lines = gtk_text_buffer_get_line_count (buffer)) > 500) {
		GtkTextIter start,end;

		gtk_text_buffer_get_iter_at_line(buffer,&start,0);
		gtk_text_buffer_get_iter_at_line(buffer,&end,lines-500);
		gtk_text_buffer_delete(buffer,&start,&end);
		gtk_text_buffer_get_end_iter(buffer,iter);
	}
	gtk_text_buffer_insert(buffer,iter,txt,-1);
	//gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(textView),iter,0.0,FALSE,0.5,0.0);
}

void *thread_log_handler(void *arg) {

	GraphFrame *graph = frame.graph;
	WebStatPtr webstat = frame.webstat;

	GtkWidget *textView = frame.textView;
	GtkTextIter iter;
	GtkTextBuffer *buffer = NULL;

	char *logfile = (char *)arg;
	FILE *log;
	char *status;
	int len;
	char tmp[LOG_BUFFER_SIZE];
	char *line;
	char *lline;

	time_t edate = 0,
		ldate = 0;
	int fdate = 0;

	assert(graph!=NULL);
	assert(webstat!=NULL);
	assert(textView!=NULL);

	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW (textView));
	gtk_text_buffer_get_start_iter(buffer,&iter);
	
	if(!(log = fopen(logfile,"r")))
		perror_exit("%s %d: ",__FILE__,__LINE__);

	// seek to the end of the file
	if(fseek(log,0,SEEK_END)<0)
		perror_exit("%s %d: ",__FILE__,__LINE__);

	while(1) {
		tmp[0] = '\0';
		len = 0;
		status = fgets(tmp,LOG_BUFFER_SIZE,log);
		len = strlen(tmp);
		while((status==NULL)||(tmp[len-1]!='\n')) {
			sleep(1);
			status = fgets(&(tmp[len]),LOG_BUFFER_SIZE-len,log);
			len = strlen(tmp);
		}
		
		line = strdup(tmp);

		if(edate)
			ldate = edate;

		insert_text(buffer,textView,&iter,tmp);
		
		pthread_mutex_lock(&mutex);
		{
			if((lline = process_log_line(line,webstat,graph,&edate,&fdate)))
				check_watches(lline);
		}
		pthread_mutex_unlock(&mutex);

		free(line);
		if(lline)
			free(lline);	
	}

	return NULL;
}

void *thread_usr_handler(void *arg) {
	char *request = NULL;
	int sd = 0;
	cmd_ret *c = NULL;

	char *log = (char *)arg;
	
	assert(arg != NULL);
	sd = *((int *)arg);
	free(arg);
	
	while((request = request_read(sd))) {
		assert(request != NULL);
		if(request[0] == '\0')
			break;

		pthread_mutex_lock(&mutex);
		{
			c = parse(request);
		}
		pthread_mutex_unlock(&mutex);

		assert(c != NULL);
		reply_send(sd,c->data_type,c->data);

		free(request);
		destroy_return(c);
	}
	return  NULL;
}

void w3ld_init_graph(GraphFrame *gf) {
	set_dimensions(gf, GRAPH_WIDTH, GRAPH_HEIGHT);
	set_show_vertex_weight(gf, False);
	set_show_edge_weight(gf, False);
	set_shape(gf, BOX);
	set_dim_vertex(gf,BSIDE,HSIDE);
	set_scale(gf, 0);
	set_flag_draw(gf, True);
	set_num_vertex(gf, 0);
	set_num_ver_group(gf, 0);
	set_count_vertex(gf,0);
	set_method_tree(gf,BFS_TREE);
	set_graph_dir(gf, DIRECTED);
}

void *
w3ld(void *arg)
{

	options opts;
	char *log = NULL;
	pthread_t tid_log;
	pthread_t tid_usr;

	int tmpsd;
	struct sockaddr_in sin;
	int slen;
	int *sd = NULL;

	GtkWidget *textView;

	textView    = (GtkWidget *)((void **)arg)[0];
	opts.base   = strdup((char *)((void **)arg)[1]);
	opts.domain = strdup((char *)((void **)arg)[2]);
	opts.prefix = strdup((char *)((void **)arg)[3]);
	opts.port   = atoi((char *)((void **)arg)[4]);
	log         = strdup((char *)((void **)arg)[5]);
	
	free((void **)arg);

	// void init_everything()
	{
		frame.graph = create_current_graph();
		w3ld_init_graph(frame.graph);
		frame.webstat = new_web_stat();
		frame.textView = textView;
		frame.watches = init_linked_list();
	
		LOGR_set_base_webhost(frame.webstat,opts.base);
		LOGR_set_url_prefix(frame.webstat,opts.prefix);
		LOGR_set_local_domain(frame.webstat,opts.domain);
		LOGR_set_logs_filetype(frame.webstat,LOGR_EXTENDED_LOG);
		LOGR_set_resolve_timeout(frame.webstat,RESOLVE_TIMEOUT_DEFAULT);
		LOGR_set_compute_user_sessions(frame.webstat);
	}

	pthread_mutex_init(&mutex,NULL);

	if(pthread_create(&tid_log,NULL,thread_log_handler,(void *)log) < 0)
		perror_exit("%s %c",__FILE__,__LINE__);

	if(!create_socket(&opts,&tmpsd,&sin,&slen)) {
		GtkTextBuffer *buffer = NULL;
		GtkTextIter iter;
		buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW (textView));
		gtk_text_buffer_get_start_iter(buffer,&iter);
		gtk_text_buffer_insert(buffer,&iter,"Bind failed. Please Try again with a different port, or free the given port.",-1);
		return NULL;
	}

	// Load user functions
	load_user_functions();

	// Wait for and handle users
	while(1) {
		if(!(sd = (int *)malloc(sizeof(int))))
			perror_exit("%s %d",__FILE__,__LINE__);
		if((*sd = accept(tmpsd, (struct sockaddr *)&sin, &slen)) < 0)
			perror_exit("%s %d"__FILE__,__LINE__);
		if(pthread_create(&tid_usr,NULL,thread_usr_handler,(void *)sd) < 0)
			perror_exit("%s %d",__FILE__,__LINE__);
	}

	pthread_mutex_unlock(&mutex);
	if(pthread_mutex_destroy(&mutex) == EBUSY) {
		fprintf(stderr,"Mutex is busy, aborting anyway\n");
		abort();
	}

	return NULL;
}
